package com.anlogic.sdk.programflash.utils;

import java.io.File;

import org.apache.logging.log4j.Logger;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.ui.console.MessageConsole;

import com.anlogic.sdk.openocd.OpenocdInitConfig;
import com.anlogic.sdk.openocd.OpenocdInitParam;
import com.anlogic.sdk.openocd.model.JtagCable;
import com.anlogic.sdk.openocd.model.RemoteTarget;
import com.anlogic.sdk.programflash.ProgramFlashModel;
import com.anlogic.sdk.utils.EnvUtils;
import com.anlogic.sdk.utils.FileUtils;
import com.anlogic.sdk.utils.ICommandLauncher;
import com.anlogic.sdk.utils.LogUtils;
import com.anlogic.sdk.utils.ProcLauncherUtils;
import com.anlogic.sdk.utils.StringUtils;
import com.anlogic.sdk.utils.UtilsPlugin;
import com.anlogic.sdk.utils.options.ChipType;

public class ProgramFlashUtils {
	private static Logger logger = LogUtils.getLogger(ProgramFlashUtils.class);
	
	private static final String LAUNCH_DOWNLOAD_SERVER = "launch_download_server";
	
	private static final String DOWNLOAD_SERVER_SCRIPT_FILE = EnvUtils.getIDERootPath() + "/tools/launch_download_server";
	
	public static final String PROGRAM_FLASH_SCRIPT_FILE = EnvUtils.getIDERootPath() + "/tools/data/program_flash.tcl";
	
	public static String getScriptFileName(String baseName) {
		String scriptFileName = baseName;
		if (!Platform.getOS().equals("linux")) {
			scriptFileName += ".bat";
		}
		return scriptFileName;
	}
	
	public static void writeScript(String filePath, String cmd) {
		if (Platform.getOS().equals("linux")) {
			cmd = "#!/bin/bash\n\n" + cmd;
		}
		FileUtils.write(new File(filePath), cmd);
	}
	
	public static void programFlash(ProgramFlashModel model) {
		File pwd = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
		MessageConsole downloadConsole = UtilsPlugin.getDefault().getMessageConsole("Download Tool");
		ICommandLauncher downloadLauncher = UtilsPlugin.getDefault().getLauncher(downloadConsole);
		
		downloadLauncher.setFilterErrorMessage(true);
		
		String chipType = model.getChip();
		String port = model.getPort();
		
		if (ChipType.isDr1m90(chipType)) {
			int temp = Integer.valueOf(port).intValue() + 1;
			port = String.valueOf(temp);
		}
		if (ChipType.isSOC66080(chipType)) {
			int temp = Integer.valueOf(port).intValue() + 3;
			port = String.valueOf(temp);
		}
		
		String programFlashCmd = "";
		if (model.isLocal()) {
			String.format("program_flash %s %s %s", model.getHost(), port, chipType);
		} else {
			programFlashCmd = String.format("program_flash_remote %s %s %s %s %s", model.getHost(), port, chipType, model.getImage(), model.getOffset());
		}
		
		String cmd = programFlashCmd;
		
		Job job = new Job("Download Tool executing...") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				logger.info("Program flash starting...");
				downloadLauncher.asyncLaunch(cmd, pwd, null);
				while (!downloadLauncher.hasTerminated()) {
					try {
						Thread.sleep(100L);
					} catch (InterruptedException e) {
						logger.error("Unexpected error occurred {}", e.getMessage());
					}
					if (monitor.isCanceled()) {
						downloadLauncher.terminate();
					}
					if (downloadLauncher.hasTerminated()) {
						logger.info("Program flash done.");
					}
				}
				monitor.done();
				return Status.OK_STATUS;
			}
		};
		job.schedule();
	}
	
	public static void programFlashWithServer(ProgramFlashModel model) {
		logger.info("kill port {}", model.getPort());
		ProcLauncherUtils.killServer(model.getPort());
		logger.info("kill port done");
		
		File pwd = new File(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
		MessageConsole serverConsole = UtilsPlugin.getDefault().getMessageConsole("Download Server Tool");
		MessageConsole downloadConsole = UtilsPlugin.getDefault().getMessageConsole("Download Tool");
		ICommandLauncher serverLauncher = UtilsPlugin.getDefault().getLauncher(serverConsole);
		ICommandLauncher downloadLauncher = UtilsPlugin.getDefault().getLauncher(downloadConsole);
		
		serverLauncher.setRedirectErrToOut(true);
		serverLauncher.setFilterErrorMessage(true);
		downloadLauncher.setFilterErrorMessage(true);
		
		String chipType = model.getChip();
		
		if (ChipType.isDr1(chipType) || ChipType.isSOC66080(chipType) || ChipType.isDR1M350(chipType)) {
			logger.info("Release the lockdown status...");
			ProcLauncherUtils.releaseLockdown();
			logger.info("Release the lockdown status done");
		}
		
		String port = model.getPort();
		if (ChipType.isDr1m90(chipType)) {
			int temp = Integer.valueOf(port).intValue() + 1;
			port = String.valueOf(temp);
		}
		if (ChipType.isSOC66080(chipType)) {
			int temp = Integer.valueOf(port).intValue() + 3;
			port = String.valueOf(temp);
		}
		
		String programFlashCmd = String.format("program_flash %s %s %s", model.getHost(), port, chipType);
		
		Job job = new Job("Download Tool executing...") {
			@Override
			protected IStatus run(IProgressMonitor monitor) {
				logger.info("Program flash starting...");
				serverLauncher.asyncLaunch(LAUNCH_DOWNLOAD_SERVER, pwd, null);
				downloadLauncher.asyncLaunch(programFlashCmd, pwd, null);
				while (!serverLauncher.hasTerminated()) {
					String ret = serverLauncher.flush();
					if (!ret.isEmpty()) {
						downloadLauncher.printStdOutMessage(ret);
					}
					try {
						Thread.sleep(100L);
					} catch (InterruptedException e) {
						logger.error("Unexpected error occurred {}", e.getMessage());
					}
					if (monitor.isCanceled()) {
						downloadLauncher.terminate();
						serverLauncher.terminate();
					}
					if (downloadLauncher.hasTerminated()) {
						serverLauncher.terminate();
						logger.info("Program flash done.");
					}
				}
				monitor.done();
				return Status.OK_STATUS;
			}
		};
		job.schedule();
	}
	
	public static void writeScript(ProgramFlashModel model) {
		logger.info("Write program flash script...");
		String imageFile = FileUtils.convertToLinuxPath(model.getImage());
		String flashType = model.getType();

		int initParam = 0;
		String programCmd = "";
		switch (flashType) {
			case "qspi-x1-single": {
				initParam = OpenocdInitParam.setQspi(initParam, 1);
				programCmd = String.format("program flash %s", imageFile);
				break;
			}
			case "qspi-x4-single": {
				initParam = OpenocdInitParam.setQspi(initParam, 1);
				programCmd = String.format("program flash %s quad_en", imageFile);
				break;
			}
			case "qspi-x1-dual_stacked": {
				initParam = OpenocdInitParam.setQspi(initParam, 1);
				initParam = OpenocdInitParam.setQspi1(initParam, 1);
				programCmd = String.format("program flash %s", imageFile);
				break;
			}
			case "qspi-x4-dual_stacked": {
				initParam = OpenocdInitParam.setQspi(initParam, 1);
				initParam = OpenocdInitParam.setQspi1(initParam, 1);
				programCmd = String.format("program flash %s quad_en", imageFile);
				break;
			}
			case "nand": {
				initParam = OpenocdInitParam.setNand(initParam, 1);
				programCmd = String.format("program nand %s", imageFile);
				break;
			}
			case "emmc0": {
				initParam = OpenocdInitParam.setEmmc(initParam, 1);
				programCmd = String.format("program emmc %s", imageFile);
				break;
			}
			case "emmc1": {
				initParam = OpenocdInitParam.setEmmc1(initParam, 1);
				programCmd = String.format("program emmc %s", imageFile);
				break;
			}
			default: {
				programCmd = String.format("program flash %s", imageFile);
				break;
			}
		}
		
		if (model.getEnablePjtag()) {
			initParam = OpenocdInitParam.setPJtag(initParam, 1);
		}
		
		String initCmd = "";
		if (initParam != 0) {
			initParam = OpenocdInitParam.setRemoteDebug(initParam, 1);
			initCmd = OpenocdInitConfig.getInitParamCmd(initParam);
		}
		
		String eraseFlashCmd = "";
		if (model.getEraseFlash()) {
			String bankIdStr = "0";
			if (model.getUseCustomizeFlash()) {
				bankIdStr = model.getBankId();
			}
			if (flashType.contains("qspi")) {
				eraseFlashCmd = String.format("flash erase_sector %s 0 last", bankIdStr);
			} else if (flashType.contains("nand")) {
				eraseFlashCmd = String.format("nand erase %s", bankIdStr);
			}
		}
		
		String enableXipCmd = "";
		if (model.getEnableXip()) {
			String bankIdStr = "0";
			if (model.getUseCustomizeFlash()) {
				bankIdStr = model.getBankId();
			}
			enableXipCmd = "flash xip_init " + bankIdStr;
		}

		if (!model.getOffset().isEmpty()) {
			programCmd = programCmd + " " + model.getOffset();
		}

		if (model.getVerify()) {
			programCmd = programCmd + " verify";
		}

		String cfgFile = OpenocdInitConfig.getCfgFile(model.getChip());

		String customizeCmd = "";
		if (model.getUseCustomizeFlash()) {
			customizeCmd = String.format("flash customize %s %s %s %s %s %s %s", model.getBankId(), model.getReadCmd(),
					model.getPprogCmd(), model.getEraseCmd(), model.getPageSize(), model.getSectorSize(),
					model.getChipSize());
		}

		String preCmd = OpenocdInitConfig.getCommandLineCmd(initCmd);
		RemoteTarget target = model.getRemoteTarget();
		if (target != null) {
			JtagCable cable = target.getCable();
			if (cable != null) {
				int freq = StringUtils.parseInt(cable.getFrequency());
				initParam = OpenocdInitParam.setJtagSpeed(initParam, freq);
				initCmd = OpenocdInitConfig.getInitParamCmd(initParam);
				preCmd = OpenocdInitConfig.getCommandLineCmd(initCmd);
				preCmd = preCmd + String.format(" -c \"adapter usb location %s\"", cable.getUsbPort());
			}
		}
		preCmd = preCmd + " " + OpenocdInitConfig.getGdbServerCmd(model.getPort());
		String serverCmd = OpenocdInitConfig.getCmd(preCmd, cfgFile, "");
		writeScript(getScriptFileName(DOWNLOAD_SERVER_SCRIPT_FILE), serverCmd);
		
		if (imageFile == null || imageFile.isEmpty()) {
			programCmd = "";
		}
		
		String downloadCmd = String.format("%s\n%s\n%s\n%s\n", customizeCmd, eraseFlashCmd, programCmd, enableXipCmd);
		writeScript(PROGRAM_FLASH_SCRIPT_FILE, downloadCmd);
		
		logger.info("Write program flash script done");
	}
}
